﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Crypto.Signers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Asn1.GM;
using System.Text.RegularExpressions;
using Org.BouncyCastle.Crypto.Engines;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto.Digests;

namespace SM
{ 
    /// <summary>
    /// SM2工具类
    /// </summary>
    public class SM2Util
    {
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="publicKey_string"></param>
        /// <param name="data_string">plain</param>
        /// <returns></returns>
        public static string Encrypt(string publicKey_string, string data_string)
        {
            var publicKey = Hex.Decode(publicKey_string);
            var data = Encoding.UTF8.GetBytes(data_string);
            return Encrypt(publicKey, data);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="privateKey_string"></param>
        /// <param name="encryptedData_string">hex</param>
        /// <returns></returns>
        public static string Decrypt(string privateKey_string, string encryptedData_string)
        {
            var privateKey = Hex.Decode(privateKey_string);
            var encryptedData = Hex.Decode(encryptedData_string);
            var de_str = SM2Util.Decrypt(privateKey, encryptedData);
            string plainText = Encoding.UTF8.GetString(de_str);
            return plainText;
        }

        public static SM2KeyPair GenerateKeyPair()
        {
            SM2 sm2 = SM2.Instance;
            AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.GenerateKeyPair();
            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;
            ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;
            BigInteger privateKey = ecpriv.D;
            ECPoint publicKey = ecpub.Q;
            var result = new SM2KeyPair();
            result.PublicKey = Util.byteToHex(publicKey.GetEncoded(false));
            result.PrivateKey = Util.byteToHex(privateKey.ToByteArray());
            return result;
        }
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="publicKey"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string Encrypt(byte[] publicKey, byte[] data)
        {
            var x9ec = GMNamedCurves.GetByName("SM2P256V1");
            ICipherParameters publicKeyParameters = new ECPublicKeyParameters(x9ec.Curve.DecodePoint(publicKey), new ECDomainParameters(x9ec));

            var sm2 = new SM2Engine(new SM3Digest());
            sm2.Init(true, new ParametersWithRandom(publicKeyParameters));
            byte[] res = sm2.ProcessBlock(data, 0, data.Length);
            return ByteUtil.ToHexString(res);
        } 

        public static byte[] Decrypt(byte[] privateKey, byte[] encryptedData)
        {
            var x9ec = GMNamedCurves.GetByName("SM2P256V1");
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, privateKey), new ECDomainParameters(x9ec));

            var sm2 = new SM2Engine(new SM3Digest());
            sm2.Init(false, privateKeyParameters);
            byte[] decryptedData = sm2.ProcessBlock(encryptedData, 0, encryptedData.Length);
            return decryptedData;
        }

        /// <summary>
        /// 加签算法 标准C1C2C3模式
        /// </summary>
        /// <param name="sourceData">源数据</param>
        /// <param name="privateKey">私钥</param>
        /// <param name="userId">用户标识</param>
        /// <returns></returns>
        public static byte[] Sign(byte[] sourceData, byte[] privateKey, byte[] userId = null)
        {
            var privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, privateKey), new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
            var sm2 = new SM2Signer(new SM3Digest());
            ICipherParameters cp;
            if (userId != null) cp = new ParametersWithID(new ParametersWithRandom(privateKeyParameters), userId);
            else cp = new ParametersWithRandom(privateKeyParameters);
            sm2.Init(true, cp);
            sm2.BlockUpdate(sourceData, 0, sourceData.Length);
            return sm2.GenerateSignature();
        }
        public static bool verify(String pubk, byte[] srcData, byte[] sign)
        {
            SM2 sm2 = SM2.Instance;
            ECPoint publicKey = sm2.ecc_curve.DecodePoint(Util.hexToByte(pubk));
            ECPublicKeyParameters pubKeyParameters = new ECPublicKeyParameters(publicKey, sm2.ecc_bc_spec);
            return verify(pubKeyParameters, (byte[])null, srcData, sign);
        }

        public static bool verify(ECPublicKeyParameters pubKeyParameters, byte[] srcData, byte[] sign)
        {
            return verify(pubKeyParameters, (byte[])null, srcData, sign);
        }

        public static bool verify(ECPublicKeyParameters pubKeyParameters, byte[] withId, byte[] srcData, byte[] sign)
        {
            SM2Signer signer = new SM2Signer();
            Object param;
            if (withId != null)
            {
                param = new ParametersWithID(pubKeyParameters, withId);
            }
            else
            {
                param = pubKeyParameters;
            }

            signer.Init(false, (ICipherParameters)param);
            //signer.Update(srcData, 0, srcData.Length);
            signer.BlockUpdate(srcData, 0, srcData.Length);

            return signer.VerifySignature(sign);
        } 

    }
}


